package net.lab0.nebula.gui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

import net.lab0.fractal.classes.FractalComponent;
import net.lab0.fractal.classes.RenderingCoordinates;
import net.lab0.fractal.classes.ViewCoordinates;
import net.lab0.fractal.fractals.mandelbrot.BasicMandelbrotFractal;
import net.lab0.nebula.data.QuadTreeNode;
import net.lab0.nebula.enums.Status;

@SuppressWarnings("serial")
public class MandelbrotComponent
extends FractalComponent
implements ComponentListener
{
    private boolean                mandelbrotQuadTreeDisplayed = true;
    private int                    minimumDisplayedsQuareSize  = 8;
    private QuadTreeNode           highlightedNode;
    
    private MainWindow             mainWindow;
    private BasicMandelbrotFractal fractal;
    private boolean                mandelbrotSetDisplayed      = false;
    
    public MandelbrotComponent(MainWindow mainWindow)
    {
        super();
        fractal = new BasicMandelbrotFractal(new RenderingCoordinates(new ViewCoordinates(0, 0, 4.0, 4.0), 100, 100), 4096, 0.1f,
        TimeUnit.MILLISECONDS.toMillis(250));
        setFractal(fractal);
        fractal.startComputation();
        fractal.pauseComputation();
        
        addComponentListener(this);
        
        this.mainWindow = mainWindow;
    }
    
    // private void popupTest(MouseEvent e)
    // {
    // if (e.isPopupTrigger())
    // {
    // JMenu statsMenu = new JMenu("Statistics");
    // JMenu subdivMenu = new JMenu("Subdivide");
    //
    // mousePosition = e.getPoint();
    // RenderingCoordinates rc = fractal.getRenderingCoordinates();
    // Point2D.Double p = rc.toRenderingCoordinates(mousePosition);
    //
    // for (final DataQuadTreeNode<BasicQuadTreeElement, MandelbrotQuadTreeZoneInfo> node : mandelbrotQuadTree.getNodesInRectangle(p, p))
    // {
    // if (node != null)
    // {
    // JMenuItem currentTileStats = new JMenuItem(node.getPath());
    // statsMenu.add(currentTileStats);
    // currentTileStats.addMouseListener(new MouseAdapter()
    // {
    // @Override
    // public void mouseEntered(MouseEvent e)
    // {
    // System.out.println(node.getPath());
    // highlightedNode = node;
    // repaint();
    // }
    // });
    // currentTileStats.addActionListener(new ActionListener()
    // {
    // @Override
    // public void actionPerformed(ActionEvent e)
    // {
    // JFrame f = new MandelbrotZoneInformationFrame(node);
    // f.pack();
    // f.setVisible(true);
    // f.setExtendedState(JFrame.MAXIMIZED_BOTH);
    // f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    //
    // highlightedNode = null;
    // repaint();
    // }
    // });
    //
    // JMenu currentTileSubdivMenu = new JMenu(node.getPath());
    // currentTileSubdivMenu.addItemListener(new ItemListener()
    // {
    // @Override
    // public void itemStateChanged(ItemEvent e)
    // {
    // highlightedNode = node;
    // repaint();
    // }
    // });
    // subdivMenu.add(currentTileSubdivMenu);
    //
    // for (int i = node.getDepth() + 1; i <= Math.min(node.getTreeRoot().getMaxNodeDepth(), node.getDepth() + 10); ++i)
    // {
    // JMenuItem subdivMenuItem = new JMenuItem("to " + i);
    // currentTileSubdivMenu.add(subdivMenuItem);
    // final Integer tmpInt = new Integer(i);
    // subdivMenuItem.addActionListener(new ActionListener()
    // {
    // @Override
    // public void actionPerformed(ActionEvent e)
    // {
    // try
    // {
    // mandelbrotQuadTree.setComputingDepth(tmpInt, node);
    // highlightedNode = null;
    // repaint();
    // }
    // catch (InterruptedException e1)
    // {
    // JOptionPane.showMessageDialog(null, e1.getMessage(), "Can't compute " + e.getClass().getSimpleName(),
    // JOptionPane.ERROR_MESSAGE);
    // e1.printStackTrace();
    // }
    // }
    // });
    // }
    // }
    // }
    //
    // JPopupMenu popupMenu = new JPopupMenu("Options");
    // popupMenu.add(statsMenu);
    // popupMenu.add(subdivMenu);
    //
    // popupMenu.show(this, e.getX(), e.getY());
    // }
    // }
    
    @Override
    public void paintComponent(Graphics g)
    {
        if (mandelbrotSetDisplayed)
        {
            super.paintComponent(g);
        }
        else
        {
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, getWidth(), getHeight());
        }
        
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gs = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gs.getDefaultConfiguration();
        BufferedImage bimage = gc.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
        Graphics2D g2d = (Graphics2D) bimage.getGraphics();
        
        RenderingCoordinates view = fractal.getRenderingCoordinates();
        
        Point2D.Double p1 = view.getScaledTopLeftCorner();
        Point2D.Double p2 = view.getScaledBottomRightCorner();
        
        if (mandelbrotQuadTreeDisplayed)
        {
            // System.out.println("disp qtree");
            if (mainWindow.getQuadTreeManager() != null)
            {
                QuadTreeNode root = mainWindow.getQuadTreeManager().getQuadTreeRoot();
                int maxDisplayDepth = root.getMaxNodeDepth();
                
                Collection<QuadTreeNode> nodesInRectangle = root.getNodesInRectangle(p1, p2);
                for (QuadTreeNode node : nodesInRectangle)
                {
                    if (!node.isLeafNode() || node.depth > maxDisplayDepth)
                    {
                        continue;
                    }
                    
                    Point start = view.toScreenCoordinates(node.minX, node.maxY);
                    Point end = view.toScreenCoordinates(node.maxX, node.minY);
                    int width = end.x - start.x;
                    int height = end.y - start.y;
                    if (height < minimumDisplayedsQuareSize || width < minimumDisplayedsQuareSize)
                    {
                        maxDisplayDepth = node.depth - 1;
                        continue;
                    }
                }
                // System.out.println("maxDispDepth=" + maxDisplayDepth);
                
                for (QuadTreeNode node : nodesInRectangle)
                {
                    if (node.depth > maxDisplayDepth)
                    {
                        continue;
                    }
                    if (!node.isLeafNode() && node.depth < maxDisplayDepth)
                    {
                        continue;
                    }
                    
                    Point start = view.toScreenCoordinates(node.minX, node.maxY);
                    Point end = view.toScreenCoordinates(node.maxX, node.minY);
                    int width = end.x - start.x;
                    int height = end.y - start.y;
                    if (node.status == Status.INSIDE)
                    {
                        g2d.setColor(new Color(0f, 0f, 1.0f, 0.25f));
                        g2d.fillRect(start.x, start.y, width, height);
                        g2d.setColor(new Color(0f, 0f, 1.0f, 1f));
                        g2d.drawRect(start.x, start.y, width, height);
                    }
                    else if (node.status == Status.BROWSED)
                    {
                        if (node.children == null)
                        {
                            g2d.setColor(new Color(0f, 1f, 0f, 0.25f));
                            g2d.fillRect(start.x, start.y, width, height);
                            g2d.setColor(new Color(0f, 1f, 0f, 1f));
                            g2d.drawRect(start.x, start.y, width, height);
                        }
                        else
                        {
                            g2d.setColor(new Color(0.5f, 0f, 0.5f, 0.25f));
                            g2d.fillRect(start.x, start.y, width, height);
                            g2d.setColor(new Color(0.5f, 0f, 0.5f, 1f));
                            g2d.drawRect(start.x, start.y, width, height);
                        }
                    }
                    else if (node.status == Status.OUTSIDE)
                    {
                        g2d.setColor(new Color(1f, 0f, 0f, 0.25f));
                        g2d.fillRect(start.x, start.y, width, height);
                        g2d.setColor(new Color(1f, 0f, 0f, 0.75f));
                        g2d.drawRect(start.x, start.y, width, height);
                    }
                    else if (node.status == Status.VOID)
                    {
                        g2d.setColor(new Color(1f, 1f, 1f, 0.25f));
                        g2d.fillRect(start.x, start.y, width, height);
                        g2d.setColor(new Color(1f, 1f, 1f, 0.75f));
                        g2d.drawRect(start.x, start.y, width, height);
                    }
                    
                }
            }
        }
        
        if (highlightedNode != null)
        {
            Point start = view.toScreenCoordinates(highlightedNode.minX, highlightedNode.maxY);
            Point end = view.toScreenCoordinates(highlightedNode.maxX, highlightedNode.minY);
            int width = end.x - start.x;
            int height = end.y - start.y;
            
            g2d.setColor(new Color(0f, 1f, 1f, 0.25f));
            g2d.fillRect(start.x, start.y, width, height);
            g2d.setColor(new Color(0f, 1f, 1f, 0.75f));
            g2d.drawRect(start.x, start.y, width, height);
        }
        

        Graphics2D componentG2D = (Graphics2D) g;
        componentG2D.drawImage(bimage, 0, 0, null);
    }
    
    // @Override
    // protected void fractalComponentMouseMoved(MouseEvent e)
    // {
    // super.fractalComponentMouseMoved(e);
    //
    // RenderingCoordinates rc = fractal.getRenderingCoordinates();
    // Point2D.Double p = rc.toRenderingCoordinates(mousePosition);
    // DataQuadTreeNode<BasicQuadTreeElement, MandelbrotQuadTreeZoneInfo> node = mandelbrotQuadTree.getLeafNodeAt(p);
    // if (node != null)
    // {
    // setToolTipText(node.getPath());
    // }
    // else
    // {
    // setToolTipText("Out of bounds");
    // }
    // }
    
    public boolean isMandelbrotQuadTreeDisplayed()
    {
        return mandelbrotQuadTreeDisplayed;
    }
    
    public void setMandelbrotQuadTreeDisplayed(boolean mandelbrotQuadTreeDisplayed)
    {
        this.mandelbrotQuadTreeDisplayed = mandelbrotQuadTreeDisplayed;
    }
    
    @Override
    public void componentResized(ComponentEvent e)
    {
        fractal.restartComputation();
    }
    
    @Override
    public void componentMoved(ComponentEvent e)
    {
    }
    
    @Override
    public void componentShown(ComponentEvent e)
    {
    }
    
    @Override
    public void componentHidden(ComponentEvent e)
    {
    }
    
    public BasicMandelbrotFractal getFractal()
    {
        return fractal;
    }
    
    public void setMandelbrotSetDisplayed(boolean mandelbrotSetDisplayed)
    {
        this.mandelbrotSetDisplayed = mandelbrotSetDisplayed;
        if (mandelbrotSetDisplayed)
        {
            fractal.restartComputation();
        }
        else
        {
            fractal.pauseComputation();
        }
    }
    
}
